package com.franklin.ideaplugin.easytesting.rpc.dubbo;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson2.JSON;
import com.franklin.ideaplugin.api.classloader.ProjectClassLoader;
import com.franklin.ideaplugin.api.classloader.ProjectClassLoaderManager;
import com.franklin.ideaplugin.easytesting.common.entity.ETRsp;
import com.franklin.ideaplugin.easytesting.common.utils.PortUtils;
import com.franklin.ideaplugin.easytesting.entity.ExecuteResult;
import com.franklin.ideaplugin.easytesting.rpc.IExecuteResultHandler;
import com.franklin.ideaplugin.easytesting.rpc.entity.MethodInvokeData;
import com.intellij.psi.PsiMethod;

import java.awt.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author Franklin
 * @since 2023/08/26 17:20
 */
public class DubboRpcClient {

    public void invoke(
            PsiMethod psiMethod,
            ExecuteResult executeResult,
            MethodInvokeData methodInvokeData,
            DubboConfig dubboConfig,
            IExecuteResultHandler executeResultHandler
    ) {
        //获取类加载器
        ProjectClassLoader projectClassLoader = getClassLoader(psiMethod);
        ClassLoader originLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(projectClassLoader);

        try {
            com.franklin.ideaplugin.easytesting.common.entity.MethodInvokeData invokeData = new com.franklin.ideaplugin.easytesting.common.entity.MethodInvokeData();
            BeanUtil.copyProperties(methodInvokeData, invokeData);
            DubboInvokerManager dubboInvokerManager = DubboInvokerManager.getInstance(psiMethod.getProject());
            String interfaceClassName = psiMethod.getContainingClass().getQualifiedName();
            DubboInvoker dubboInvoker = dubboInvokerManager.getInvoker(
                    interfaceClassName,
                    dubboConfig,
                    () -> new DubboInvoker(projectClassLoader, getPort(), dubboConfig, interfaceClassName)
            );

            Object result = dubboInvoker.invoke(invokeData);
            Thread.currentThread().setContextClassLoader(originLoader);
            ETRsp<?> etRsp = null;
            if (result instanceof Throwable) {
                etRsp = ETRsp.fail(((Throwable) result));
            } else {
                etRsp = ETRsp.success(result);
            }
            executeResult.setEtRsp(etRsp);
            String resultPath = executeResult.getCachePath() + "/result";
            resultPath = resultPath.replace('\\', '/');
            FileUtil.writeString(JSON.toJSONString(etRsp), resultPath, StandardCharsets.UTF_8);
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            Thread.currentThread().setContextClassLoader(originLoader);
            EventQueue.invokeLater(() -> {
                executeResultHandler.handleResult(executeResult);
            });
        }


    }

    private Integer getPort() {
        int port = this.hashCode() % 65535 + 1;
        try {
            return PortUtils.getUsablePort(port);
        } catch (IOException e) {
            return port;
        }
    }

    private static ProjectClassLoader getClassLoader(PsiMethod psiMethod) {
        return ProjectClassLoaderManager.getClassLoader(psiMethod.getProject());
    }
}
